﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Windows.Forms;
using Rsdn.SmartApp;

namespace Rsdn.Janus
{
    /// <summary>
    /// Обработчик команд форума.
    /// </summary>
    [CommandTarget]
    internal sealed class ForumCommandTarget : CommandTarget
    {
        public ForumCommandTarget(IServiceProvider serviceProvider)
            : base(serviceProvider)
        {
        }

        [CommandExecutor("Janus.Forum.OpenForumOnRsdn")]
        public void ExecuteOpenForumOnRsdn(
            ICommandContext context, int? forumId)
        {
            context.OpenUrlInBrowser(
                SiteUrlHelper.GetForumUrl(GetForum(context, forumId).Name));
        }

        [CommandExecutor("Janus.Forum.ShowForumArticles")]
        public void ExecuteShowForumArticles(
            ICommandContext context, int? forumId)
        {
            context.OpenUrlInBrowser(
                JanusProtocolDispatcher.FormatURI(
                    JanusProtocolResourceType.ArticleList,
                    GetForumId(context, forumId).ToString()));
        }

        [CommandExecutor("Janus.Forum.ShowForumFaq")]
        public void ExecuteShowForumFaq(ICommandContext context, int? forumId)
        {
            context.OpenUrlInBrowser(
                JanusProtocolDispatcher.FormatURI(
                    JanusProtocolResourceType.FaqList,
                    GetForumId(context, forumId).ToString())
                );
        }

        [CommandExecutor("Janus.Forum.WriteMessage")]
        public void ExecuteWriteMessage(ICommandContext context, int? forumId)
        {
            MessageInfo messageInfo = new MessageInfo(GetForumId(context, forumId));
            MessageEditor.EditMessage(ServiceProvider, MessageFormMode.Add, messageInfo);
        }

        [CommandExecutor("Janus.Forum.SetAllMessagesInForumReadMark")]
        public void ExecuteSetAllMessagesInForumReadMark(ICommandContext context, int[] forumIds, bool isRead)
        {
            if (Config.Instance.ConfirmationConfig.ConfirmMarkAll)
            {
                string text = SR.Forum.PromptMarkAll.FormatStr(isRead ? SR.Forum.PromptRead : SR.Forum.PromptUnread);

                DialogResult dialogResult = MessageBox.Show(
                    context.GetRequiredService<IUIShell>().GetMainWindowParent(),
                    text,
                    ApplicationInfo.ApplicationName,
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question,
                    MessageBoxDefaultButton.Button2);

                if (dialogResult != DialogResult.Yes)
                {
                    return;
                }
            }

            Action<IProgressVisualizer> action = info => ForumHelper.SetForumsReadMark(context, GetForums(context, forumIds), isRead);

            ProgressWorker.Run(context, false, action);
        }

        [CommandStatusGetter("Janus.Forum.SetAllMessagesInForumReadMark")]
        public CommandStatus QuerySetAllMessagesInForumReadMarkStatus(
            ICommandContext context, int[] forumIds, bool isRead)
        {
            CommandStatus commandStatus1 = QueryForumsCommandStatus(context, forumIds);

            Func<bool> condition1 = () => context.GetService<IMessageMarkService>() != null;

            Func<bool> condition2 = () => GetForums(context, forumIds).Any(forum => forum.CanSetForumReadMark(isRead));
            CommandStatus commandStatus2 = commandStatus1.UnavailableIfNot(condition1);

            return commandStatus2.DisabledIfNot(condition2);
        }

        private static CommandStatus QueryForumsCommandStatus(ICommandContext context, int[] forumIds)
        {
            if (forumIds != null)
            {
                return CommandStatus.Normal;
            }

            var activeForumSvc = context.GetService<IActiveForumService>();
            return activeForumSvc != null && activeForumSvc.ActiveForum != null
                ? CommandStatus.Normal
                : CommandStatus.Unavailable;
        }

        [CommandStatusSubscriber("Janus.Forum.SetAllMessagesInForumReadMark")]
        public IDisposable SubscribeSetAllMessagesInForumReadMarkStatusChanged(
            IServiceProvider provider, Action handler)
        {
            IEventBroker eventBroker = provider.GetRequiredService<IEventBroker>();

            Action<ForumEntryChangedEventArgs> action = args =>
                                {
                                    if ((args.ChangeType & ForumEntryChangeType.ReadMark) != 0)
                                    {
                                        handler();
                                    }
                                };
            IObserver<ForumEntryChangedEventArgs> nextAction = Observer.Create<ForumEntryChangedEventArgs>(action);
            return eventBroker.Subscribe(ForumEventNames.AfterForumEntryChanged, nextAction);
        }


        [CommandExecutor("Janus.Forum.MessageExport")]
        public void ExecuteMessageExport(ICommandContext context)
        {
            MessageExporter.Export(context);
        }

        [CommandStatusGetter("Janus.Forum.MessageExport")]
        public CommandStatus QueryMessageExportStatus(ICommandContext context)
        {
            CommandStatus commandStatus = ForumMessageCommandHelper.GetMultipleMessagesCommandStatus(context, null);
            CommandStatus forumCommandStatus = QueryForumCommandStatus(context, null);
            return commandStatus.Or(forumCommandStatus);
        }

        [CommandStatusSubscriber("Janus.Forum.MessageExport")]
        public IDisposable SubscribeMessageExportStatusChanged(IServiceProvider serviceProvider, Action handler)
        {
            return ForumMessageCommandHelper.SubscribeMessageCommandStatusChanged(serviceProvider, handler);
        }


        [CommandStatusGetter("Janus.Forum.OpenForumOnRsdn")]
        [CommandStatusGetter("Janus.Forum.ShowForumArticles")]
        [CommandStatusGetter("Janus.Forum.ShowForumFaq")]
        [CommandStatusGetter("Janus.Forum.WriteMessage")]
        public CommandStatus QueryForumCommandStatus(
            ICommandContext context, int? forumId)
        {
            if (forumId != null)
            {
                return CommandStatus.Normal;
            }

            IActiveForumService activeForumSvc = context.GetService<IActiveForumService>();
            return activeForumSvc != null && activeForumSvc.ActiveForum != null
                ? CommandStatus.Normal
                : CommandStatus.Unavailable;
        }

        [CommandStatusSubscriber("Janus.Forum.OpenForumOnRsdn")]
        [CommandStatusSubscriber("Janus.Forum.ShowForumArticles")]
        [CommandStatusSubscriber("Janus.Forum.ShowForumFaq")]
        [CommandStatusSubscriber("Janus.Forum.WriteMessage")]
        [CommandStatusSubscriber("Janus.Forum.SetAllMessagesInForumReadMark")]
        [CommandStatusSubscriber("Janus.Forum.MessageExport")]
        public IDisposable SubscribeForumCommandStatusChanged(
            IServiceProvider provider, Action handler)
        {
            IActiveForumService activeForumSvc = provider.GetService<IActiveForumService>();
            if (activeForumSvc != null)
            {
                SmartApp.EventHandler<IActiveForumService> statusUpdater =
                    delegate
                        {
                            handler();
                        };

                activeForumSvc.ActiveForumChanged += statusUpdater;
                Action action = () => activeForumSvc.ActiveForumChanged -= statusUpdater;

                return Disposable.Create(action);
            }

            return Disposable.Empty;
        }

        private static IForum GetForum(IServiceProvider provider, int? forumId)
        {
            IActiveForumService activeForumService = provider.GetRequiredService<IActiveForumService>();

            return forumId != null
                ? Forums.Instance[forumId.Value]
                : activeForumService.ActiveForum;
        }

        private static IEnumerable<IForum> GetForums(IServiceProvider provider, int[] forumIds)
        {
            IForum activeForum = provider.GetRequiredService<IActiveForumService>().ActiveForum;

            return forumIds != null
                ? (IEnumerable<IForum>)forumIds.Select(id => Forums.Instance[id])
                : new[] { activeForum };
        }

        private static int GetForumId(IServiceProvider provider, int? forumId)
        {
            if (forumId != null)
            {
                return forumId.Value;
            }

            IActiveForumService activeForumService = provider.GetRequiredService<IActiveForumService>();
            IForum currentForum = activeForumService.ActiveForum;
            return currentForum != null ? currentForum.ID : -1;
        }
    }
}